//
// Copyright (c) 2009 All Right Reserved
//
// vl
//
// 2009-01-01
// Contains ...
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Xml.Serialization;
using LargoCommon.Abstract;
using LargoCommon.Interfaces;
using LargoCommon.Midi;
namespace LargoCommon.Music
{
///
/// Musical Strike Collection.
///
[Serializable]
[XmlRoot]
public sealed class MusicalStrikeCollection : Collection
{
#region Constructors
///
/// Initializes a new instance of the MusicalStrikeCollection class.
///
public MusicalStrikeCollection()
{
}
///
/// Initializes a new instance of the MusicalStrikeCollection class.
///
/// Given list.
public MusicalStrikeCollection(IList givenList)
: base(givenList)
{
}
///
/// Initializes a new instance of the class.
///
/// The Xml tones.
/// The rhythmic order.
public MusicalStrikeCollection(XElement xmlTones, byte rhythmicOrder)
{
Contract.Requires(xmlTones != null);
byte lastInstrument = (byte)MidiMelodicInstrument.None;
foreach (var xtone in xmlTones.Elements()) {
IMusicalTone mt = null;
switch (xtone.Name.ToString()) {
case "Tone": {
mt = new MusicalTone(xtone, rhythmicOrder);
break;
}
case "Pause": {
mt = new MusicalPause(xtone, rhythmicOrder);
break;
}
case "Tick": {
mt = new MusicalStrike(xtone, rhythmicOrder);
break;
}
}
if (mt == null) {
continue;
}
//// 2019/02- instrument is also property of pauses ...
if (mt.InstrumentNumber == (byte)MidiMelodicInstrument.None) {
mt.InstrumentNumber = lastInstrument;
}
lastInstrument = mt.InstrumentNumber;
//// mt.BarNumber = LibSupport.ReadIntegerAttribute(xtone.Attribute("BarNumber"));
this.Add(mt);
}
}
#endregion
#region Properties - Xml
/// Gets String representation of the object.
/// Returns value.
public XElement GetXElement {
get
{
var xtones = new XElement("Tones", null);
foreach (var xtone in this.Select(mt => mt.GetXElement).Where(xtone => xtone != null)) {
xtones.Add(xtone);
}
return xtones;
}
}
#endregion
#region Properties
///
/// Gets or sets Name ( there was an Identifier in the Harmonic Stream Ground class ...).
///
/// Property description.
public string Name { get; set; }
///
/// Gets Count Of MelTones.
///
/// Property description.
public int CountOfMelTones
{
get
{
if (this.Count == 0) {
return 0;
}
var num = (from mt in this where mt.ToneType == MusicalToneType.Melodic select 1).Count();
return num;
}
}
///
/// Gets the count of sounding tones.
///
/// Property description.
public int CountOfSoundingTones
{
get
{
if (!this.HasAnySoundingTone) {
return 0;
}
var num = (from mt in this
where mt.ToneType == MusicalToneType.Melodic && !mt.IsPause
select 1).Count();
return num;
}
}
/// Gets Mean Loudness.
/// Property description.
public MusicalLoudness MeanLoudness
{
get
{
const int limit = 100;
float sumLoudness = 0;
var numNotes = 0;
var tones = this.Where(mt => mt != null && mt.ToneType != MusicalToneType.Empty);
foreach (var mt in tones) {
if (mt is MusicalStrike mtone && mtone.Loudness > 0) {
sumLoudness += (short)mtone.Loudness;
numNotes++;
if (numNotes >= limit) {
break;
}
}
}
var meanLoudness = numNotes != 0 ? sumLoudness / numNotes : 0;
return (MusicalLoudness)(byte)Math.Round(meanLoudness);
}
}
///
/// Gets the mean midi key number.
///
///
/// The mean midi key number.
///
public byte MeanMidiKeyNumber
{
get
{
const int limit = 50; //// .Take(50)
float sumMidiKeys = 0;
var numNotes = 0;
// ReSharper disable once LoopCanBePartlyConvertedToQuery
foreach (var musicalStrike in this.Where(musicalStrike => musicalStrike != null && !musicalStrike.IsPause)) {
MusicalTone musicalTone = musicalStrike as MusicalTone;
if (musicalTone == null || musicalTone.IsEmpty) {
continue;
}
sumMidiKeys += musicalTone.Pitch.MidiKeyNumber;
numNotes++;
if (numNotes >= limit) {
break;
}
}
var meanMidiKey = numNotes != 0 ? sumMidiKeys / numNotes : 0;
return (byte)Math.Round(meanMidiKey);
}
}
///
/// Gets the mean octave.
///
/// Property description.
public MusicalOctave MeanOctave
{
get
{
const int limit = 50; //// .Take(50)
float sumOctave = 0;
var numNotes = 0;
// ReSharper disable once LoopCanBePartlyConvertedToQuery
foreach (var musicalStrike in this.Where(musicalStrike => musicalStrike != null && !musicalStrike.IsPause)) {
MusicalTone musicalTone = musicalStrike as MusicalTone;
if (musicalTone == null || musicalTone.IsEmpty) {
continue;
}
sumOctave += musicalTone.Pitch.Octave;
numNotes++;
if (numNotes >= limit) {
break;
}
}
var meanOctave = numNotes != 0 ? sumOctave / numNotes : 0;
return (MusicalOctave)(byte)Math.Round(meanOctave);
}
}
///
/// Gets the first instrument.
///
/// Property description.
public MidiMelodicInstrument FirstMelodicInstrument
{
get
{
var instrument = MidiMelodicInstrument.None;
// ReSharper disable once LoopCanBePartlyConvertedToQuery
foreach (var musicalStrike in this.Where(musicalStrike => musicalStrike != null && !musicalStrike.IsPause)) {
MusicalTone musicalTone = musicalStrike as MusicalTone;
if (musicalTone == null || musicalTone.IsEmpty) {
continue;
}
instrument = (MidiMelodicInstrument)musicalTone.InstrumentNumber;
break;
}
return instrument;
}
}
///
/// Gets the first rhythmic instrument.
///
///
/// The first rhythmic instrument.
///
public MidiRhythmicInstrument FirstRhythmicInstrument
{
get
{
var instrument = MidiRhythmicInstrument.None;
// ReSharper disable once LoopCanBePartlyConvertedToQuery
foreach (var musicalStrike in this.Where(musicalStrike => musicalStrike != null && !musicalStrike.IsPause)) {
instrument = (MidiRhythmicInstrument)musicalStrike.InstrumentNumber;
break;
}
return instrument;
}
}
///
/// Gets a value indicating whether [has any sounding tone].
///
/// Property description.
///
/// True if [has any sounding tone]; otherwise, false.
///
public bool HasAnySoundingTone
{
get
{
return this.Count != 0 && this.Any(mt => !mt.IsPause);
}
}
///
/// Gets a value indicating whether this instance has true tones.
///
///
/// true if this instance has true tones; otherwise, false.
///
public bool HasTrueTones
{
get
{
foreach (var musicalStrike in this.Where(musicalStrike => musicalStrike != null && !musicalStrike.IsPause)) {
if (!(musicalStrike is MusicalTone musicalTone) || musicalTone.IsEmpty) {
return false;
}
}
return true;
}
}
///
/// Gets the melodic pattern identifier.
///
///
/// The melodic pattern identifier.
///
public string MelodicPatternIdentifier
{
get
{
var sb = new StringBuilder("Idents");
foreach (var ident in this.Select(mt => mt.MelodicIdentifier).Where(xtone => xtone != null)) {
sb.Append(ident);
}
return sb.ToString();
}
}
///
/// Gets the rhythmic pattern identifier.
///
///
/// The rhythmic pattern identifier.
///
public string RhythmicPatternIdentifier
{
get
{
var sb = new StringBuilder("Idents");
foreach (var ident in this.Select(mt => mt.RhythmicIdentifier).Where(xtone => xtone != null)) {
sb.Append(ident);
}
return sb.ToString();
}
}
#endregion
#region Public static
///
/// Gets the tones from Midi tones.
///
/// The given header.
/// The midi tones.
/// if set to true [melodic track].
/// Returns value.
public static MusicalStrikeCollection GetTones(MusicalHeader givenHeader, IMidiTones midiTones, bool melodicTrack)
{ //// this.Strip.Context.Header
Contract.Requires(midiTones != null);
//// Contract.Requires(track.Sequence != null);
Contract.Requires(givenHeader.Metric.MetricGround > 0);
if (midiTones == null) { //// || this.MusicalBlock.Metric.MetricGround == 0
return null;
}
var rhythmicOrder = givenHeader.System.RhythmicOrder;
//// !!!!! int division = track.Sequence.Division;
//// int division = this.MusicalBlock.Division;
//// track.BarDivision = MusicalProperties.BarDivision(division, this.MusicalBlock.MetricBeat, this.MusicalBlock.MetricGround);
//// rhythmicOrder must be integer divisor of barDivision
var quotient = midiTones.BarDivision / rhythmicOrder;
//// !!!!! if (track.MidiTones.Count == 0 && track.Events.Count < 0) { track.ReadMidiTones(track); }
var mtones = new List();
if ((midiTones.List.Count == 0) || (quotient == 0)) {
return null;
}
//// int numberOfBarsInTrack = midiTones.NumberOfBars;
//// this.MusicalBlock.NumberOfBars = Math.Max(this.MusicalBlock.NumberOfBars, numberOfBarsInTrack);
//// int test = 0;
foreach (var midiTone in midiTones.List) {
for (var barNumber = midiTone.BarNumberFrom; barNumber <= midiTone.BarNumberTo; barNumber++) {
var realBarNumber = midiTones.FirstBarNumber + barNumber - 1;
//// if (midiTone.Note == "A#1" && realBarNumber >= 3) { //// && this.MusicalBlock.NumberOfBars == 32
//// test = 1; }
//// test = 2 * test;
var musTone = MusicalStrike.GetNewMusicalTone(givenHeader, quotient, midiTone, barNumber, realBarNumber, melodicTrack);
if (musTone == null) {
continue;
}
mtones.Add(musTone);
}
}
var selectedTones = (from tone in mtones orderby tone.BarNumber, tone.BitFrom select tone).ToList();
var tones = new MusicalStrikeCollection(selectedTones);
//// if (tones == null) { return null; }
var completedTones = tones.CollectionWithAddedMissingPauses();
//// if (completedTones == null) { return null; }
var standardizedTones = completedTones.StandardizeTones(givenHeader);
return standardizedTones;
}
#endregion
#region Public methods
///
/// Add on musical tone to the end of part.
///
/// Musical tone.
/// If set to true [set ordinal index].
public void AddTone(IMusicalTone givenTone, bool setOrdinalIndex)
{
Contract.Requires(givenTone != null);
//// if (givenTone == null) { return false; }
if (setOrdinalIndex) {
givenTone.OrdinalIndex = this.Count;
}
this.Add(givenTone);
}
///
/// Adds the collection.
///
/// The given tones.
/// If set to true [set ordinal index].
public void AddCollection(MusicalStrikeCollection givenTones, bool setOrdinalIndex)
{
Contract.Requires(givenTones != null);
givenTones.ForAll(musicalStrike => this.AddTone(musicalStrike, setOrdinalIndex));
}
///
/// Adds the collection.
///
/// The given tones.
/// The given bar number.
/// if set to true [set ordinal index].
public void AddCollection(MusicalStrikeCollection givenTones, int givenBarNumber, bool setOrdinalIndex)
{
Contract.Requires(givenTones != null);
foreach (var tone in givenTones) {
tone.BarNumber = givenBarNumber;
}
this.AddCollection(givenTones, setOrdinalIndex);
}
///
/// Add Missing Pauses.
///
/// if set to true [reset bar].
///
/// Returns value.
///
public MusicalStrikeCollection Clone(bool resetBar)
{
var newTones = new MusicalStrikeCollection();
foreach (var newTone in this.Select(tone => (IMusicalTone)tone.CloneTone())) {
if (resetBar) {
newTone.BarNumber = 1;
}
newTones.Add(newTone);
}
return newTones;
}
/// Return number of bits with non-harmonic tones.
/// Returns value.
/// Harmonic structure.
/// Rhythmic range.
public byte NumberOfMelodicBits(
BinaryStructure harmonicStruct,
BitRange rhythmicRange)
{
Contract.Requires(harmonicStruct != null);
Contract.Requires(rhythmicRange != null);
//// if (harmonicStruct == null) { return 0; }
if (rhythmicRange == null) {
return 0;
}
var tones = from IMusicalTone mt in this where mt != null && !mt.IsPause select mt as MusicalTone;
var interRanges = from MusicalTone mt in tones
where mt != null && !mt.IsEmpty && !harmonicStruct.IsOn(mt.Pitch.Element)
select mt.BitRange into toneRange //// (barNumber)
select rhythmicRange.IntersectionWith(toneRange) into interRange
where interRange != null
select interRange;
var totalLength = interRanges.Aggregate(0, (current, interRange) => (byte)(current + interRange.Length));
return totalLength;
}
///
/// Add Missing Pauses.
///
/// Returns value.
[JetBrains.Annotations.PureAttribute]
public MusicalStrikeCollection CollectionWithAddedMissingPauses()
{
var firstTone = this.FirstOrDefault();
if (firstTone == null) {
return null;
}
var lastBarNumber = firstTone.BarNumber - 1;
var rhythmicOrder = firstTone.RhythmicOrder;
//// Pause for previous bars
var newTones = CollectionWithPauses(lastBarNumber, rhythmicOrder);
//// Pause before the first tone
if (firstTone.BitFrom > 0) {
IMusicalTone pause = MusicalPause.CreatePause(firstTone.RhythmicOrder, 0, firstTone.BitFrom, firstTone.BarNumber);
pause.InstrumentNumber = firstTone.InstrumentNumber; //// 2019/02
newTones.Add(pause);
}
newTones.Add(firstTone);
var lastTone = firstTone;
this.AddMissingInternalPauses(lastTone, newTones);
return newTones;
}
///
/// Standardizes the tones.
///
/// The given header.
///
/// Returns value.
///
[SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1126:PrefixCallsCorrectly", Justification = "Reviewed.")]
[JetBrains.Annotations.PureAttribute]
public MusicalStrikeCollection StandardizeTones(MusicalHeader givenHeader)
{
var newTones = new MusicalStrikeCollection();
var beat = givenHeader.Metric.MetricBeat;
if (beat == 0) {
return null;
}
var ground = givenHeader.Metric.MetricGround;
int rorder = givenHeader.System.RhythmicOrder;
var wholeNoteTicks = rorder * ground / beat;
MusicalTone lastMelodicTone = null;
foreach (var tone in this) {
//// Include pause to the previous tone.
if (tone is MusicalTone musicalTone && !musicalTone.IsEmpty) {
newTones.Add(musicalTone);
lastMelodicTone = musicalTone;
continue;
}
NoteLength noteLength;
if (lastMelodicTone != null) {
var musicalPause = tone as MusicalPause ?? new MusicalPause(tone.BitRange, tone.BarNumber);
noteLength = NoteLength.GetNoteLength(lastMelodicTone.Duration, musicalPause.Duration, wholeNoteTicks);
lastMelodicTone.NoteLength = noteLength;
if (noteLength != null && noteLength.IncludePause && lastMelodicTone.Pause == null) { //// added lastMelodicTone.Pause == null !?!
lastMelodicTone.Pause = musicalPause;
}
else {
noteLength = NoteLength.GetNoteLength(musicalPause.Duration, 0, wholeNoteTicks);
musicalPause.NoteLength = noteLength;
newTones.Add(musicalPause);
}
}
else {
noteLength = NoteLength.GetNoteLength(tone.Duration, 0, wholeNoteTicks);
tone.NoteLength = noteLength;
newTones.Add(tone);
}
}
return newTones;
}
///
/// Determine RhythmicStructure.
///
/// Rhythmical order.
/// Returns value.
public RhythmicStructure DetermineRhythmicStructure(byte rhythmicOrder)
{
var tonesGroups = from mt in this group mt by mt.BitFrom;
var tonesDistinct = new Collection();
foreach (var toneGroup in tonesGroups) {
tonesDistinct.Add(toneGroup.First());
}
var rstruct = new RhythmicStructure(rhythmicOrder, tonesDistinct);
rstruct.DetermineBehavior();
return rstruct;
}
///
/// Write tones into MIDI-list.
///
/// Midi Event List.
/// Melodic instrument.
/// Bar division.
public void WriteTo(MidiEventCollection midiEvents, byte instrument, int barDivision)
{ //// , MidiChannel channel
////byte rhyOrder,
if (midiEvents == null) {
return;
}
foreach (var mt in this) {
MusicalStrike mtone = mt as MusicalStrike;
if (mtone == null) {
continue;
}
mtone.InstrumentNumber = instrument;
//// mt.Channel = channel;
mt.WriteTo(midiEvents, barDivision);
}
}
///
/// Export To Midi Events.
///
/// Midi Events.
/// Midi instrument.
/// Midi channel.
/// Bar division.
public void ExportToMidiEvents(MidiEventCollection events, byte instrument, MidiChannel channel, int barDivision)
{
Contract.Requires(events != null);
if (events == null) {
return;
}
if (channel != MidiChannel.DrumChannel) {
events.PutInstrument(0, instrument);
}
foreach (var mt in this.Where(mt => mt != null && mt.RhythmicOrder > 0)) {
MusicalStrike mtone = mt as MusicalStrike;
if (mtone == null) {
continue;
}
mtone.InstrumentNumber = instrument;
//// mt.Channel = channel;
mt.WriteTo(events, barDivision);
}
}
///
/// Sets the instrument.
///
/// The instrument.
/// The octave.
public void SetInstrument(byte instrument, MusicalOctave octave)
{
var firstTone = this.FirstOrDefault();
foreach (var mt in this) {
//// 2015/01
// ReSharper disable once PossibleUnintendedReferenceComparison
MusicalStrike mtone = mt as MusicalStrike;
if (mtone == null) {
continue;
}
// ReSharper disable once PossibleUnintendedReferenceComparison
if (mtone.IsFromPreviousBar && mtone == firstTone) {
continue;
}
mtone.InstrumentNumber = instrument;
//// mt.Channel = (MidiChannel)channel;
if (mtone is MusicalTone melt && !melt.IsEmpty) {
melt.Pitch.Octave = (short)octave;
}
}
}
#endregion
#region String representation
///
/// String with tones.
///
/// Returns value.
public string TonesToString()
{
var s = new StringBuilder();
var actualBarNumber = 0;
foreach (var mt in this.Where(mt => mt != null)) {
if (mt.BarNumber != actualBarNumber) {
s.Append("|| "); ////‡↕#∫$
actualBarNumber = mt.BarNumber;
}
s.Append(mt);
}
return s.ToString();
}
///
/// Rhythmic tones to string.
///
/// Returns value.
public string RhythmicTonesToString()
{
var s = new StringBuilder();
var actualBarNumber = 0;
foreach (var mt in this.Where(mt => mt != null)) {
if (mt.BarNumber != actualBarNumber) {
s.Append("|| "); ////‡↕#∫$
actualBarNumber = mt.BarNumber;
}
if (mt is MusicalStrike mx) {
s.Append(mx.RhythmicToString());
}
}
return s.ToString();
}
///
/// Returns the tooltip.
///
/// Returns value.
public string TooltipString()
{
var s = new StringBuilder();
this.ForAll(musicalStrike => s.AppendLine(musicalStrike.ToString()));
return s.ToString();
}
/// String representation of the object.
/// Returns value.
public override string ToString()
{
var s = new StringBuilder();
this.ForAll(musicalStrike => s.Append(musicalStrike));
return s.ToString();
}
#endregion
#region Private methods
///
/// Collections the with pauses.
///
/// The last bar number.
/// The rhythmic order.
/// Returns value.
private static MusicalStrikeCollection CollectionWithPauses(int lastBarNumber, byte rhythmicOrder)
{
var newTones = new MusicalStrikeCollection();
for (var barNumber = 1; barNumber <= lastBarNumber; barNumber++) {
IMusicalTone pause = MusicalPause.CreatePause(rhythmicOrder, 0, rhythmicOrder, barNumber);
newTones.Add(pause);
}
return newTones;
}
///
/// Adds the missing internal pauses.
///
/// The last tone.
/// The new tones.
private void AddMissingInternalPauses(IMusicalTone lastTone, MusicalStrikeCollection newTones)
{
foreach (var tone in this) {
//// Pause between the last tone and tone
int bitFrom;
int duration;
if (lastTone.BarNumber == tone.BarNumber) {
bitFrom = lastTone.BitFrom + lastTone.Duration;
duration = tone.BitFrom - bitFrom;
//// tone.BitPosition - lastTone.BitPosition;
//// 2016/08 - in the property Pause is saved short pause after tone (staccato,...)
var mtone = lastTone as MusicalTone;
if (duration > 0 && (mtone?.Pause == null)) {
IMusicalTone pause = MusicalPause.CreatePause(lastTone.RhythmicOrder, (byte)bitFrom, (byte)duration, lastTone.BarNumber);
pause.InstrumentNumber = lastTone.InstrumentNumber; //// 2019/02
newTones.Add(pause);
}
}
else {
if (lastTone.BarNumber < tone.BarNumber) {
//// Pause after the last tone of previous bar in the previous bar
bitFrom = lastTone.BitFrom + lastTone.Duration;
duration = lastTone.RhythmicOrder - bitFrom;
if (duration > 0) {
IMusicalTone pause = MusicalPause.CreatePause(lastTone.RhythmicOrder, (byte)bitFrom, (byte)duration, lastTone.BarNumber);
pause.InstrumentNumber = lastTone.InstrumentNumber; //// 2019/02
newTones.Add(pause);
}
//// 2016/08
if (tone.BitFrom > 0) {
bitFrom = 0;
duration = tone.BitFrom;
IMusicalTone pause = MusicalPause.CreatePause(lastTone.RhythmicOrder, (byte)bitFrom, (byte)duration, lastTone.BarNumber);
pause.InstrumentNumber = lastTone.InstrumentNumber; //// 2019/02
newTones.Add(pause);
}
}
}
//// ReSharper disable once PossibleUnintendedReferenceComparison
//// tone != lastTone)
if (!object.ReferenceEquals(tone, lastTone)) {
newTones.Add(tone);
}
lastTone = tone;
}
}
#endregion
}
}